using System;
using System.Xml;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;

namespace Dida.Test
{
	public interface IParamProvider
	{
		IEnumerable Values { get; }
		int NumParams { get; }
	}

	class ValueParamProvider : IParamProvider
	{
		object val;

		public ValueParamProvider(Type type, XmlReader xmlReader)
		{
			string value = xmlReader.GetAttribute("value");
			if (value == null)
				throw new XmlAttributeNotSpecifiedException(xmlReader, "value");

			try
			{
				val = ParsersRegister.Default.Parse(type, value);
			}
			catch (Exception err)
			{
				throw new XmlAttributeException(err, xmlReader, "value");
			}

			// not interested in any child content
			xmlReader.Skip();
		}

		public IEnumerable Values
		{
			get { yield return val; }
		}

		public int NumParams
		{
			get { return 1; }
		}
	}

	class FloatRangeParamProvider : IParamProvider
	{
		float min;
		float max;
		float step;
		bool maxInclusive = false;

		public FloatRangeParamProvider(XmlReader reader)
		{
			min = XmlReadingTools.ReadAttributeFloat(reader, "min");
			max = XmlReadingTools.ReadAttributeFloat(reader, "max");
			step = XmlReadingTools.ReadAttributeFloat(reader, "step");

			string maxInclusiveStr = reader.GetAttribute("maxInclusive");
			if (maxInclusiveStr != null)
			{
				if (!bool.TryParse(maxInclusiveStr, out maxInclusive))
				{
					throw new XmlAttributeException("\"" + maxInclusiveStr +
						"\" is not a valid boolean value", reader, "maxInclusive");
				}
			}

			reader.Skip();		// not interested in the children
		}

		public IEnumerable Values
		{
			get
			{
				if (maxInclusive)
				{
					for (float cur = min; cur <= max; cur += step)
					{
						yield return cur;
					}
				}
				else
				{
					for (float cur = min; cur < max; cur += step)
					{
						yield return cur;
					}
				}
			}
		}

		public int NumParams
		{
			get 
			{
				float range = max - min;
				int ret = (int)(range / step);

				if (maxInclusive && Math.IEEERemainder(range, step) == 0)
					ret++;

				return ret;
			}
		}
	};

	class IntRangeParamProvider : IParamProvider
	{
		int min;
		int max;
		int step;
		bool maxInclusive = false;

		public IntRangeParamProvider(XmlReader reader)
		{
			min = XmlReadingTools.ReadAttributeInt(reader, "min");
			max = XmlReadingTools.ReadAttributeInt(reader, "max");
			step = XmlReadingTools.ReadAttributeInt(reader, "step");

			string maxInclusiveStr = reader.GetAttribute("maxInclusive");
			if (maxInclusiveStr != null)
			{
				if (!bool.TryParse(maxInclusiveStr, out maxInclusive))
				{
					throw new XmlAttributeException("\"" + maxInclusiveStr +
						"\" is not a valid boolean value", reader, "maxInclusive");
				}
			}

			reader.Skip();		// not interested in the children
		}

		public IEnumerable Values
		{
			get
			{
				if (maxInclusive)
				{
					for (int cur = min; cur < max; cur += step)
					{
						yield return cur;
					}
				}
				else
				{
					for (int cur = min; cur < max; cur += step)
					{
						yield return cur;
					}
				}
			}
		}

		public int NumParams
		{
			get
			{
				int range = max - min;
				int ret = range / step;

				if (maxInclusive)
				{
					int remaineder;
					Math.DivRem(range, step, out remaineder);
					if (remaineder == 0)
						ret++;
				}

				return ret;
			}
		}
	}
}
